/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __HOUSE_H__
#define __HOUSE_H__

#include <map>
#include <vector>
#include <threads/threads.h>
#include <threads/Lock.h>

namespace liba
{

template<class Na, class RepRep>
class Shared
{
public:
	typedef liba::Shared<Na, RepRep> this_type;
	typedef Na name_type;
	Shared()
	:	rep_it_valid(false)
		//rep_it( 0 ) // BUG by standart, but no other simple ideas
	{}
	virtual ~Shared()
	{
		unload_rep();
	}

	Shared(const Shared & other)
	:	rep_it(other.rep_it),
		rep_it_valid(other.rep_it_valid)
	{
		if(rep_it_valid)
			rep_it->second->ref_count++;
	}

	Shared & operator=(const Shared & other)
	{
		if(this != &other)
		{
			unload_rep();
			rep_it = other.rep_it;
			rep_it_valid = other.rep_it_valid;
			if(rep_it_valid)
				rep_it->second->ref_count++;
		}

		return *this;
	}

	class Rep : public RepRep
	{
//MSVC fails to compile this
//		friend class Shared<Na,RepRep>;
	public:
		int ref_count;
	public:
		explicit Rep( const Na & name )
			:	RepRep( name ),
				ref_count( 0 )
		{}
	};
	Rep * get_rep()
	{
		return !rep_it_valid ? 0 : rep_it->second;
	}
	const Rep * get_rep()const
	{
		return !rep_it_valid ? 0 : rep_it->second;
	}
	const Na & get_rep_name()const
	{
		return rep_it->first;
	}
	void load_rep( const Na & name )
	{
		if( !rep_it_valid )
		{
			rep_it = static_load_rep( name );
			rep_it_valid = true;
		}
	}
	void unload_rep()
	{
		if( rep_it_valid )
		{
			static_unload_rep( rep_it );
			rep_it_valid = false;
		}
	}
protected:
	typedef std::map<Na, Rep *> House;

	typename House::iterator rep_it;
	bool rep_it_valid;	//MS guys fixed rep_it(0) standard uncomplaint workaround
	static House * house;

private:
	static typename House::iterator static_load_rep( const Na & name )
	{
		if( !house )
			house = new House;
		typename House::iterator it = house->find( name );
		if( it == house->end() )
			it = house->insert( std::make_pair( name, new Rep(name) ) ).first;
		++(it->second->ref_count);
		return it;
	}
	static void static_unload_rep( typename House::iterator it )
	{
		if( --(it->second->ref_count) == 0 )
		{
			delete it->second;
			house->erase( it );
		}
		if( house->empty() )
		{
			delete house; house = 0;
		}
	}
};

template<class Na, class RepRep>
typename Shared<Na,RepRep>::House * Shared<Na,RepRep>::house = 0;

class SharedPrecachedRepBase
{
protected:
	bool loaded;
public:
	SharedPrecachedRepBase()
		:	loaded( true )
	{}
	void start_loading()
	{
		loaded = false;
	}
	void stop_loading()
	{
		loaded = true;
	}
	bool is_loaded()const
	{
		return loaded;
	}
};

/** This template requires method void RepRep::load() */
template<class Na, class RepRep>
class SharedPrecached : public Shared<Na,RepRep>
{
	typedef liba::Shared<Na,RepRep> Shared;
public:
	static int load_reps(__int64 start_load_time, float time_to_load)
	{
		if( !Shared::house )
			return 0;
		int loading_count = 0;
		typename Shared::House::iterator it = Shared::house->begin();
		bool skip_load = false;
		for( ;it != Shared::house->end(); ++it )
		{
			if( !it->second->is_loaded() )
			{
				if(!skip_load)
					it->second->load( it->first );

				++loading_count;

				if(!skip_load && (threads::get_precise_tick_count() - start_load_time)*threads::get_precise_tick_time() > time_to_load)
					skip_load = true;
			}
		}
		return loading_count;
	}

/*	class LoaderIterator
	{
		typename House::iterator it;
	public:
		explicit LoaderIterator(typename House::iterator it):
			it( it )
		{}
		bool operator==(const LoaderIterator & lo)const
		{
			return it == lo.it;
		}
		bool operator!=(const LoaderIterator & lo)const
		{
			return !operator==(lo);
		}
		LoaderIterator & operator++()
		{
			++it;
			return *this;
		}
		bool is_loaded()const
		{
			return it->second->is_loaded();
		}
		void load()
		{
			if( !it->second->is_loaded() )
				it->second->load( it->first );
		}
	};

	static LoaderIterator begin()
	{
		return LoaderIterator( !house ? 0 : house->begin() );
	}
	static LoaderIterator end()
	{
		return LoaderIterator( !house ? 0 : house->end() );
	}*/
};


} // namespace liba
using namespace liba;

#endif //__HOUSE_H__
